/*
 * Copyright 2025 LinQingYing. and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of this source code is governed by the Apache License 2.0,
 * which allows users to freely use, modify, and distribute the code,
 * provided they adhere to the terms of the license.
 *
 * The software is provided "as-is", and the authors are not responsible for
 * any damages or issues arising from its use.
 *
 */

package org.cangnova.cangjie.descriptors.impl

import org.cangnova.cangjie.descriptors.*
import org.cangnova.cangjie.descriptors.annotations.Annotations
import org.cangnova.cangjie.name.Name
import org.cangnova.cangjie.name.OperatorNameConventions
import org.cangnova.cangjie.types.TypeSubstitutor
import org.cangnova.cangjie.types.extractParameterNameFromFunctionTypeArgument

class FunctionInvokeDescriptor private constructor(
    container: DeclarationDescriptor,
    original: FunctionInvokeDescriptor?,
    callableKind: CallableMemberDescriptor.Kind,
//    isSuspend: Boolean
) : SimpleFunctionDescriptorImpl(
    container,
    original,
    Annotations.EMPTY,
    OperatorNameConventions.INVOKE,
    callableKind,
    SourceElement.NO_SOURCE
) {
    init {
        this.isOperator = true
//        this.isSuspend = isSuspend
        this.setHasStableParameterNames(false)
    }


    override fun doSubstitute(configuration: CopyConfiguration): FunctionDescriptor? {
        val substituted = super.doSubstitute(configuration) as FunctionInvokeDescriptor? ?: return null
        if (substituted.valueParameters.none { it.type.extractParameterNameFromFunctionTypeArgument() != null }) return substituted
        val parameterNames = substituted.valueParameters.map { it.type.extractParameterNameFromFunctionTypeArgument() }
        return substituted.replaceParameterNames(parameterNames)
    }

    override fun createSubstitutedCopy(
        newOwner: DeclarationDescriptor,
        original: FunctionDescriptor?,
        kind: CallableMemberDescriptor.Kind,
        newName: Name?,
        annotations: Annotations,
        source: SourceElement
    ): FunctionDescriptorImpl {
        return FunctionInvokeDescriptor(newOwner, original as FunctionInvokeDescriptor?, kind )
    }



    private fun replaceParameterNames(parameterNames: List<Name?>): FunctionDescriptor {
        val indexShift = valueParameters.size - parameterNames.size
        assert(indexShift == 0 || indexShift == 1) // indexShift == 1 for extension function type
        if (indexShift == 0 && parameterNames.zip(valueParameters).all { (name, parameter) -> name == parameter.name }) {
            return this
        }

        val newValueParameters = valueParameters.map {
            var newName = it.name
            val parameterIndex = it.index
            val nameIndex = parameterIndex - indexShift
            if (nameIndex >= 0) {
                val parameterName = parameterNames[nameIndex]
                if (parameterName != null) {
                    newName = parameterName
                }
            }
            it.copy(this, newName, parameterIndex)
        }

        val copyConfiguration = newCopyBuilder(TypeSubstitutor.EMPTY)
            .setHasSynthesizedParameterNames(parameterNames.any { it == null })
            .setValueParameters(newValueParameters)
            .setOriginal(original)

        return super.doSubstitute(copyConfiguration)!!
    }

    companion object Factory {
        fun create(functionClass: FunctionClassDescriptor): FunctionInvokeDescriptor {
            val typeParameters = functionClass.declaredTypeParameters

            val result = FunctionInvokeDescriptor(functionClass, null, CallableMemberDescriptor.Kind.DECLARATION)
            result.initialize(
                null,
                functionClass.thisAsReceiverParameter,
                listOf(), listOf(),

                typeParameters .subList(0, typeParameters.size - 1)
                    .withIndex()
                    .map { createValueParameter(result, it.index, it.value) },
                typeParameters.last().defaultType,
                Modality.FINAL,
                DescriptorVisibilities.PUBLIC
            )
            result.setHasSynthesizedParameterNames(true)
            return result
        }

        private fun createValueParameter(
            containingDeclaration: FunctionInvokeDescriptor,
            index: Int,
            typeParameter: TypeParameterDescriptor
        ): ValueParameterDescriptor {
            val name = when (val typeParameterName = typeParameter.name.asString()) {
                "T" -> "instance"
                "E" -> "receiver"
                else -> {
                    // Type parameter "P1" -> value parameter "p1", "P2" -> "p2", etc.
                    typeParameterName.lowercase()
                }
            }

            return ValueParameterDescriptorImpl(
                containingDeclaration, null, index,
                Annotations.EMPTY,
                Name.identifier(name),
                false,
                typeParameter.defaultType,
                isDefaultValue = false,

                SourceElement.NO_SOURCE
            )
        }
    }
}
